package com.lovesher.payment.service.resposity.bean;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;

import com.lovesher.payment.service.resposity.dao.PaymentSerialDao;
import com.lovesher.payment.service.resposity.dao.PluginConfigDao;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.lovesher.payment.common.enums.NotifyMethod;
import com.lovesher.payment.common.enums.PayStatus;
import com.lovesher.payment.common.enums.PayType;
import com.lovesher.payment.common.resposity.bean.AccountRechargeDTO;
import com.lovesher.payment.common.resposity.bean.PaymentSerial;
import com.lovesher.payment.common.resposity.bean.PluginConfig;
import com.lovesher.payment.common.resposity.bean.TransferDTO;
import com.lovesher.payment.common.resposity.bean.TransferResultDTO;
import com.lovesher.payment.service.resposity.dao.PaymentSerialDao;
import com.lovesher.payment.service.resposity.dao.PluginConfigDao;

/**
 * @Title: PaymentPlugin.java
 * 
 * @Package com.rm.pay.resposity.bean
 * 
 * @Description: 支付插件
 * 
 * @version V1.0
 * 
 */
@SuppressWarnings("deprecation")
public abstract class PaymentInterface implements Comparable<PaymentInterface> {

	/** 支付方式名称属性名称 */
	public static final String PAYMENT_NAME_ATTRIBUTE_NAME = "paymentName";

	/** 手续费类型属性名称 */
	public static final String FEE_TYPE_ATTRIBUTE_NAME = "feeType";

	/** 手续费属性名称 */
	public static final String FEE_ATTRIBUTE_NAME = "fee";

	/** LOGO属性名称 */
	public static final String LOGO_ATTRIBUTE_NAME = "logo";

	/** 描述属性名称 */
	public static final String DESCRIPTION_ATTRIBUTE_NAME = "description";

	/** 银行编码 */
	public static final String BANK_PARAMETER_NAME = "bank";
	
	/** 是否含有查询任务 **/
	public boolean isQueryTask = false;
	/**
	 * 手续费类型
	 */
	public enum FeeType {

		/** 按比例收费 */
		scale,

		/** 固定收费 */
		fixed
	}

	/**
	 * 请求方法
	 */
	public enum RequestMethod {

		/** POST */
		post,

		/** GET */
		get
	}

	@Resource(name = "pluginConfigDao")
	private PluginConfigDao pluginConfigDao;
	
	@Resource(name = "paymentSerialDao")
	protected PaymentSerialDao paymentSerialDao;
	
	@Resource(name = "systemBean")
	private SystemBean systemBean;
	
	/**
	 * 获取ID
	 * 
	 * @return ID
	 */
	public final String getId() {
		return getClass().getAnnotation(Component.class).value();
	}

	/**
	 * 获取名称
	 * 
	 * @return 名称
	 */
	public abstract String getName();

	/**
	 * 获取版本
	 * 
	 * @return 版本
	 */
	public abstract String getVersion();

	/**
	 * 获取作者
	 * 
	 * @return 作者
	 */
	public abstract String getAuthor();

	/**
	 * 是否含有查询任务
	 * @Title: getIsQueryTask 
	 * @Description: 是否含有查询任务
	 * @author tupx 
	 * @date 2017年9月19日 上午11:11:38 
	 * @version V1.0
	 */
	public boolean getIsQueryTask(){
		return this.isQueryTask;
	}
	
	public void setIsQueryTask(boolean isQueryTask){
		this.isQueryTask = isQueryTask;
	}
	/**
	 * 获取网址
	 * 
	 * @return 网址
	 */
	public abstract String getSiteUrl();

	/**
	 * 获取插件配置
	 * 
	 * @return 插件配置
	 */
	public PluginConfig getPluginConfig() {
		return pluginConfigDao.findByPluginId(getId());
	}

	/**
	 * 获取是否已启用
	 * 
	 * @return 是否已启用
	 */
	public boolean getIsEnabled() {
		PluginConfig pluginConfig = getPluginConfig();
		return pluginConfig != null ? pluginConfig.getIsEnabled() : false;
	}

	/**
	 * 获取排序
	 * 
	 * @return 排序
	 */
	public Integer getOrder() {
		PluginConfig pluginConfig = getPluginConfig();
		return pluginConfig != null ? pluginConfig.getOrders() : null;
	}

	/**
	 * 获取请求URL
	 * 
	 * @return 请求URL
	 */
	public abstract String getRequestUrl();

	/**
	 * 获取请求方法
	 * 
	 * @return 请求方法
	 */
	public abstract RequestMethod getRequestMethod();

	/**
	 * 获取请求字符编码
	 * 
	 * @return 请求字符编码
	 */
	public abstract String getRequestCharset();

	/**
	 * 获取请求参数
	 * 
	 * @param sn
	 *            编号
	 * @param description
	 *            描述
	 * @param request
	 *            httpServletRequest
	 * @return 请求参数
	 */
	public abstract Map<String, String> getParameterMap(AccountRechargeDTO accountRechargeDTO,String serialSn);

	/**
	 * 查询订单结果
	 * 
	 * @param sn
	 *            编号
	 * @param description
	 *            描述
	 * @param request
	 *            httpServletRequest
	 * @return 请求参数
	 */
	public abstract Boolean queryOrderResult(PaymentSerial paymentSerial);
	
	/**
	 * 验证通知是否合法
	 * 
	 * @param sn
	 *            编号
	 * @param notifyMethod
	 *            通知方法
	 * @param request
	 *            httpServletRequest
	 * @param paymentSerial 
	 * @return 通知是否合法
	 */
	public abstract Map<String,Object> verifyNotify(String sn, NotifyMethod notifyMethod, HttpServletRequest request, PaymentSerial paymentSerial);

	/**
	 * 获取通知返回消息
	 * 
	 * @param sn
	 *            编号
	 * @param notifyMethod
	 *            通知方法
	 * @param request
	 *            httpServletRequest
	 * @return 通知返回消息
	 */
	public abstract String getNotifyMessage(String sn, NotifyMethod notifyMethod, HttpServletRequest request);

	/**
	 * 获取超时时间
	 * 
	 * @return 超时时间
	 */
	public abstract Integer getTimeout();

	/**
	 * 获取通知URL
	 * 
	 * @param id
	 *            流水号ID
	 * @param notifyMethod
	 *            通知方法
	 * @return 通知URL
	 */
	protected String getNotifyUrl(String sn,Long userId,NotifyMethod notifyMethod,PayType payType) {
		String urlPath = systemBean.getNotifyUrlPrex();
		if (notifyMethod == null) {
			return urlPath + "/payment/notify/" + NotifyMethod.general + "/" + sn + "/" + userId + ".cgi";
		}
		if(notifyMethod == NotifyMethod.sync){
			if(payType == PayType.RECHARGE){
				return urlPath = systemBean.getReturnUrlPrex() + "/#/profile";
			}
		}
		urlPath = urlPath + "/payment/notify/" + notifyMethod + "/" + sn + "/" + userId + ".cgi";
		return urlPath;
	}
	
	
	/**
	 * 获取通知URL
	 * 
	 * @param id
	 *            流水号ID
	 * @param notifyMethod
	 *            通知方法
	 * @return 通知URL
	 */
	protected String getRefundNotifyUrl(String sn,Long userId,NotifyMethod notifyMethod,PayType payType) {
		String urlPath = systemBean.getNotifyUrlPrex();
		if (notifyMethod == null) {
			return urlPath + "/payment/refundNotify/" + NotifyMethod.general + "/" + sn + "/" + userId + ".cgi";
		}
		urlPath = urlPath + "/payment/refundNotify/" + notifyMethod + "/" + sn + "/" + userId + ".cgi";
		return urlPath;
	}
	/**
	 * 获取通知URL
	 * 
	 * @param id
	 *            流水号ID
	 * @param notifyMethod
	 *            通知方法
	 * @return 通知URL
	 */
	protected String getAppNotifyUrl(String sn,Long userId,NotifyMethod notifyMethod,PayType payType,Long repayRecordId) {
		String urlPath = systemBean.getNotifyUrlPrex();
		urlPath = urlPath + "/payment/notify/" + notifyMethod + "/" + sn + "/" + userId + ".cgi";
		return urlPath;
	}
	/**
	 * 获取通知URL
	 * 
	 * @param id
	 *            流水号ID
	 * @param notifyMethod
	 *            通知方法
	 * @return 通知URL
	 */
	protected String getNotifyUrl(String orderSn,String sn,Long userId,NotifyMethod notifyMethod,PayType payType) {
		return getNotifyUrl(sn, userId, notifyMethod, payType);
	}

	/**
	 * 连接Map键值对
	 * 
	 * @param map
	 *            Map
	 * @param prefix
	 *            前缀
	 * @param suffix
	 *            后缀
	 * @param separator
	 *            连接符
	 * @param ignoreEmptyValue
	 *            忽略空值
	 * @param ignoreKeys
	 *            忽略Key
	 * @return 字符串
	 */
	protected String joinKeyValue(Map<String, Object> map, String prefix, String suffix, String separator, boolean ignoreEmptyValue, String... ignoreKeys) {
		List<String> list = new ArrayList<String>();
		if (map != null) {
			for (Entry<String, Object> entry : map.entrySet()) {
				String key = entry.getKey();
				String value = ConvertUtils.convert(entry.getValue());
				if (StringUtils.isNotEmpty(key) && !ArrayUtils.contains(ignoreKeys, key) && (!ignoreEmptyValue || StringUtils.isNotEmpty(value))) {
					list.add(key + "=" + (value != null ? value : ""));
				}
			}
		}
		return (prefix != null ? prefix : "") + StringUtils.join(list, separator) + (suffix != null ? suffix : "");
	}

	/**
	 * 连接Map值
	 * 
	 * @param map
	 *            Map
	 * @param prefix
	 *            前缀
	 * @param suffix
	 *            后缀
	 * @param separator
	 *            连接符
	 * @param ignoreEmptyValue
	 *            忽略空值
	 * @param ignoreKeys
	 *            忽略Key
	 * @return 字符串
	 */
	protected String joinValue(Map<String, Object> map, String prefix, String suffix, String separator, boolean ignoreEmptyValue, String... ignoreKeys) {
		List<String> list = new ArrayList<String>();
		if (map != null) {
			for (Entry<String, Object> entry : map.entrySet()) {
				String key = entry.getKey();
				String value = ConvertUtils.convert(entry.getValue());
				if (StringUtils.isNotEmpty(key) && !ArrayUtils.contains(ignoreKeys, key) && (!ignoreEmptyValue || StringUtils.isNotEmpty(value))) {
					list.add(value != null ? value : "");
				}
			}
		}
		return (prefix != null ? prefix : "") + StringUtils.join(list, separator) + (suffix != null ? suffix : "");
	}

	/**
	 * POST请求
	 * 
	 * @param url
	 *            URL
	 * @param parameterMap
	 *            请求参数
	 * @return 返回结果
	 */
	protected String post(String url, Map<String, Object> parameterMap) {
		Assert.hasText(url);
		String siteUrl = StringUtils.lowerCase(url);
		if (siteUrl.startsWith("https")) {
			return postSSL(siteUrl, parameterMap);
		}
		String result = null;
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPost httpPost = null;
		try {
			httpPost = new HttpPost(url);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			if (parameterMap != null) {
				for (Entry<String, Object> entry : parameterMap.entrySet()) {
					String name = entry.getKey();
					String value = ConvertUtils.convert(entry.getValue());
					if (StringUtils.isNotEmpty(name)) {
						nameValuePairs.add(new BasicNameValuePair(name, value));
					}
				}
			}
			httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
			HttpResponse httpResponse = httpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			result = EntityUtils.toString(httpEntity);
			EntityUtils.consume(httpEntity);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != httpPost) {
				httpPost.abort();
			}
		}
		return result;
	}

	private String postSSL(String url, Map<String, Object> parameterMap) {
		Assert.hasText(url);
		String result = null;
		CloseableHttpClient httpClient = null;
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(1000).build();
		HttpPost httpPost = null;
		try {
			SSLContext sslContext = SSLContexts.createDefault();
			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
			httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
			httpPost = new HttpPost(url);
			httpPost.setConfig(requestConfig);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			if (parameterMap != null) {
				for (Entry<String, Object> entry : parameterMap.entrySet()) {
					String name = entry.getKey();
					String value = ConvertUtils.convert(entry.getValue());
					if (StringUtils.isNotEmpty(name)) {
						nameValuePairs.add(new BasicNameValuePair(name, value));
					}
				}
			}
			httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
			HttpResponse httpResponse = httpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			result = EntityUtils.toString(httpEntity);
			EntityUtils.consume(httpEntity);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != httpPost) {
				httpPost.abort();
			}
		}
		return result;
	}

	/**
	 * GET请求
	 * 
	 * @param url
	 *            URL
	 * @param parameterMap
	 *            请求参数
	 * @return 返回结果
	 * @throws SocketTimeoutException 
	 */
	protected String get(String url, Map<String, Object> parameterMap) throws SocketTimeoutException {
		Assert.hasText(url);
		String result = null;
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet httpGet = null;
		try {
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			if (parameterMap != null) {
				for (Entry<String, Object> entry : parameterMap.entrySet()) {
					String name = entry.getKey();
					String value = ConvertUtils.convert(entry.getValue());
					if (StringUtils.isNotEmpty(name)) {
						nameValuePairs.add(new BasicNameValuePair(name, value));
					}
				}
			}
			if("GBK".equals(parameterMap.get("encode"))){
				//请求丰付
				httpGet = new HttpGet(url + (StringUtils.contains(url, "?") ? "&" : "?") + EntityUtils.toString(new UrlEncodedFormEntity(nameValuePairs, "GBK")));
				//设置8s超时时间
				RequestConfig reqConfig = RequestConfig.custom().setSocketTimeout(8000).setConnectTimeout(8000).build();
				httpGet.setConfig(reqConfig);
			}else{
				httpGet = new HttpGet(url + (StringUtils.contains(url, "?") ? "&" : "?") + EntityUtils.toString(new UrlEncodedFormEntity(nameValuePairs, "UTF-8")));
			}
			HttpResponse httpResponse = httpClient.execute(httpGet);
			HttpEntity httpEntity = httpResponse.getEntity();
			result = EntityUtils.toString(httpEntity);
			EntityUtils.consume(httpEntity);
		} catch (SocketTimeoutException e) {
			throw new SocketTimeoutException();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != httpGet) {
				httpGet.abort();
			}
		}
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		PaymentInterface other = (PaymentInterface) obj;
		return new EqualsBuilder().append(getId(), other.getId()).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(getId()).toHashCode();
	}

	public int compareTo(PaymentInterface paymentPlugin) {
		return new CompareToBuilder().append(getOrder(), paymentPlugin.getOrder()).append(getId(), paymentPlugin.getId()).toComparison();
	}

	/**
	 * 充值支付回调验证成功处理方法
	 * 
	 * @param request
	 */
	public abstract void paySucess(PaymentSerial paymentSerial,String returnText,PayStatus payStatus,HttpServletRequest request);
	
	/**
	 * 获取回调传入的参数
	 * 
	 * @param request
	 * @return
	 */
	public Map<String, String> getReturnParameter(HttpServletRequest request) {
		Map<String, String> params = new HashMap<String, String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name =  iter.next();
			String[] values = requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			params.put(name, valueStr);
		}
		return params;
	}

	protected String formatDate(String format, Date date) {
		DateFormat df = new SimpleDateFormat(format);
		return df.format(date);
	}
	
	/**
	 * @Description: 异步通知处理 
	 * @param paymentSerial
	 * @param returnText
	 * @param payStatus
	 * @param amt 充值金额
	 */
	public void payAsyncNotify(PaymentSerial paymentSerial,String returnText,PayStatus payStatus, String amt){}

	public SystemBean getSystemBean() {
		return systemBean;
	}

	/**
	 * 转帐
	 *
	 * @param parameterMap
	 */
	public TransferResultDTO transfer(TransferDTO transferDTO){
		return  null;
	}
	
}
 